package com.cup.bddm.impala;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class ImpalaJdbc {
	static Logger logger = LoggerFactory.getLogger(ImpalaJdbc.class);

	@Value("${impala.driver}")
	private String driver;

	@Value("${impala.url}")
	private String url;

	@Value("${impala.user}")
	private String user;

	@Value("${impala.password}")
	private String password;

	/**
	 * 获取impala连接
	 */
	public Connection getConnection() throws SQLException {
		try {
			Class.forName(driver);
		} catch (ClassNotFoundException e) {
			logger.error("driver class not found", e);
		}
		return DriverManager.getConnection(url, user, password);

	}

	/**
	 * 关闭连接。正常情况下只需要关闭连接即可，因为Statement和PreparedStatement会在调用函数用被关闭，只有Connection留存。
	 */
	public void close(Connection conn) {
		try {
			if (conn != null && !conn.isClosed()) {
				conn.close();
			}
		} catch (SQLException e) {
			logger.error("close connection error", e);
		} finally {
			conn = null;
		}
	}

	/**
	 * 关闭Statement
	 */
	public void close(Statement stmt) {
		try {
			if (stmt != null) {
				stmt.close();
			}
		} catch (SQLException e) {
			logger.error("close Statement error", e);
		}
	}

	/**
	 * 关闭PreparedStatement
	 */
	public void close(PreparedStatement pst) {
		try {
			if (pst != null) {
				pst.close();
			}
		} catch (SQLException e) {
			logger.error("close PreparedStatement error", e);
		}
	}

	/**
	 * 关闭ResultSet
	 */
	public void close(ResultSet rs) {
		try {
			if (rs != null) {
				rs.close();
			}
		} catch (SQLException e) {
			logger.error("close ResultSet error", e);
		}
	}

	/**
	 * 执行非查询的sql语句，比如创建表，加载数据等等
	 */
	public int execute(String sql) throws SQLException {
		Statement stmt = null;
		Connection conn = null;
		try {
			conn = getConnection();
			stmt = conn.createStatement();
			return stmt.executeUpdate(sql);
		} catch (SQLException e) {
			throw e;
		} finally {
			close(stmt);
			close(conn);
		}
	}

	/**
	 * 执行非查询的sql语句，比如创建表，加载数据等等
	 */
	public boolean execute(String sql, Object... values) throws SQLException {
		PreparedStatement stmt = null;
		Connection conn = null;
		try {
			conn = getConnection();
			stmt = conn.prepareStatement(sql);
			setValue(stmt, values);
			return stmt.execute();
		} catch (SQLException e) {
			throw e;
		} finally {
			close(stmt);
			close(conn);
		}
	}

	/**
	 * 执行查询的sql语句, 返回ResultSet。
	 */
	public Map<String, Object> queryForMap(String sql, Object... values) throws SQLException {
		PreparedStatement stmt = null;
		Connection conn = null;
		Map<String, Object> map = null;
		try {
			conn = getConnection();
			stmt = conn.prepareStatement(sql);
			setValue(stmt, values);
			ResultSet res = stmt.executeQuery();
			ResultSetMetaData rsmd = res.getMetaData();
			int rowCnt = rsmd.getColumnCount();
			while (res.next()) {
				map = new LinkedHashMap<String, Object>(rowCnt);
				for (int i = 1; i <= rowCnt; i++) {
					map.put(rsmd.getColumnName(i), res.getObject(i));
				}
			}
			return map;
		} catch (SQLException e) {
			throw e;
		} finally {
			close(stmt);
			close(conn);
		}
	}

	/**
	 * 使用PreparedStatement查询数据，返回List集合，数据量比较小的时候用
	 */
	public List<Map<String, Object>> queryForList(String sql, Object... values) throws SQLException {
		Connection conn = null;
		PreparedStatement pst = null;
		ResultSet res = null;
		List<Map<String, Object>> list = new LinkedList<Map<String, Object>>();
		try {
			conn = getConnection();
			pst = conn.prepareStatement(sql);
			setValue(pst, values);
			res = pst.executeQuery();
			Map<String, Object> map = null;
			ResultSetMetaData rsmd = res.getMetaData();
			int rowCnt = rsmd.getColumnCount();
			while (res.next()) {
				map = new LinkedHashMap<String, Object>(rowCnt);
				for (int i = 1; i <= rowCnt; i++) {
					map.put(rsmd.getColumnName(i), res.getObject(i));
				}
				list.add(map);
			}
		} catch (SQLException e) {
			throw e;
		} finally {
			close(res);
			close(pst);
			close(conn);
		}
		return list;
	}

	private void setValue(PreparedStatement pst, Object... values) throws SQLException {
		for (int i = 0; i < values.length; i++) {
			if (values[i] instanceof String) {
				pst.setString(i + 1, String.valueOf(values[i]));
			}
			if (values[i] instanceof Integer) {
				pst.setInt(i + 1, Integer.valueOf(values[i].toString()));
			}
		}
	}

}
